﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Xml.Linq;
using System.Data;
using DbSharp.Framework.DbObject;
using DbSharp.Framework.Driver;

namespace DbSharp.Framework.CaseFile.Parser
{
    /// <summary>
    /// Extract all arguments in procedureDelcaration/SqlDeclaration config section 
    /// </summary>
    class StatementDeclarationArgsExtractor
    {
        #region Private members

        private XElement m_DeclarationArgsRootElement = null;
        private bool m_IsSqlStatement;
        private string m_ProviderName;
        private string m_ConnectionString;
        #endregion

        public StatementDeclarationArgsExtractor(XElement declarationArgsRootElement, string providerName, string connectionString, bool isSqlStatement)
        {
            m_DeclarationArgsRootElement = declarationArgsRootElement;
            m_ProviderName = providerName;
            m_ConnectionString = connectionString;
            m_IsSqlStatement = isSqlStatement;
            Extract();
        }


        #region Private methods

        private List<StatementArgument> m_ListArgument = new List<StatementArgument>();

        private void Extract()
        {
            m_ListArgument.Clear();
            if (m_DeclarationArgsRootElement == null)
                return;
            int tempInt;
            var q = from arg in m_DeclarationArgsRootElement.Elements("argument")
                    let sizeAttribute = arg.Attribute("size")
                    let sizeIsInteger = sizeAttribute == null ? false : int.TryParse(sizeAttribute.Value.Trim(), out tempInt)
                    select new
                    {
                        Name = arg.Attribute("name").Value.Trim(),
                        Direction = arg.Attribute("direction").Value.Trim(),
                        TypeStr = arg.Attribute("type").Value.Trim(),
                        Size = sizeIsInteger ? int.Parse(sizeAttribute.Value.Trim()) : 0,
                    };


            foreach (var arg in q)
            {
                var argumentObj = new StatementArgument
                {
                    DirectionStr = arg.Direction,
                    TypeStr = arg.TypeStr,
                    //Name = arg.Name,
                    Name = StatementDeclarationExt.NormalizeArgumentName(m_ProviderName, m_ConnectionString, arg.Name),
                    Size = arg.Size,
                };


                if ((m_IsSqlStatement) && (argumentObj.Direction != ArgumentDirection.Input))
                {
                    //indicate the argument is virtual return argument of sql statement
                    argumentObj.IsSqlStmtResultArg = true;
                }
                else
                    argumentObj.IsSqlStmtResultArg = false;


                m_ListArgument.Add(argumentObj);
            }

        }

        #endregion


        #region Public properties/methods

        public ReadOnlyCollection<StatementArgument> ListArgument
        {
            get
            {
                return new ReadOnlyCollection<StatementArgument>(m_ListArgument);
            }
        }

        #endregion
    }



    /// <summary>
    /// extract all arguments in procedure/sql  InputArguments config section 
    /// 
    /// </summary>
    class StatementInputArgsProcessor
    {
        #region Private members

        XElement m_InputArgsRootElement;
 
        IStatementDeclaration m_StatementWithInArgValues;

        #endregion

        public StatementInputArgsProcessor(XElement inputArgsRootElement,IStatementDeclaration statementDeclaration)
        {
            if (inputArgsRootElement != null)
            {
                if ((statementDeclaration == null) &&
                    (inputArgsRootElement.Elements("argument").Count()>0))
                    throw new ArgumentNullException("statementDeclaration");
            }

            m_InputArgsRootElement = inputArgsRootElement; 
            
            m_StatementWithInArgValues = statementDeclaration.CloneStatement(); 

            Extract();
        }

        #region Private methods

        private void Extract()
        {
            if (m_InputArgsRootElement == null)
                return;

            var q = from arg in m_InputArgsRootElement.Elements("argument")
                    let valueAttribute = arg.Attribute("value")
                    let valueIsReferFieldNameAttr = arg.Attribute("valueIsReferFieldName")
                    let valueFieldInLookupStatementAttr = arg.Attribute("valueFieldInSourceStatement")
                    let dateTimeFormatAttr = arg.Attribute("dateTimeFormat")
                    select new
                    {
                        Name = arg.Attribute("name").Value.Trim(),
                        Value = valueAttribute == null ? null : valueAttribute.Value,
                        ValueIsReferFieldName = valueIsReferFieldNameAttr == null ? false : valueIsReferFieldNameAttr.Value.Trim().ToUpper() == "TRUE",
                        ValueFieldInLookupStatement = valueFieldInLookupStatementAttr == null ? null : valueFieldInLookupStatementAttr.Value.Trim().ToUpper(),
                        DateTimeFormat = dateTimeFormatAttr == null ? null : dateTimeFormatAttr.Value.Trim(),
                    };


            //override the argument's value of the m_ProcDeclaration
            foreach (var item in q)
            {
                StatementArgument argObj = m_StatementWithInArgValues.FindArgument(item.Name);
                if (argObj != null)
                {
                    argObj.DataTimeFormat = item.DateTimeFormat;
                    if (item.ValueIsReferFieldName)
                    {
                        argObj.ValueFieldInLookupStatement = item.Value;
                        argObj.ValueNeedLookUp = true;
                    }
                    else
                    {
                        argObj.Value = item.Value;
                        argObj.ValueNeedLookUp = false;
                    }
                }
            }
        }


        
        #endregion

        /// <summary>
        /// brandnew Statement object, but it is with in arguments' value
        /// </summary>
        public IStatementDeclaration StatementWithInArgValues
        {
            get { return m_StatementWithInArgValues; }
        }
    }

}
